package com.tianji.learning.service.impl;

import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.client.remark.RemarkClient;
import com.tianji.api.client.user.UserClient;
import com.tianji.api.dto.user.UserDTO;
import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.domain.query.PageQuery;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.BooleanUtils;
import com.tianji.common.utils.ObjectUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.dto.ReplyDTO;
import com.tianji.learning.domain.po.InteractionQuestion;
import com.tianji.learning.domain.po.InteractionReply;
import com.tianji.learning.domain.query.ReplyPageQuery;
import com.tianji.learning.domain.vo.ReplyVO;
import com.tianji.learning.mapper.InteractionReplyMapper;
import com.tianji.learning.mq.message.PointsMessage;
import com.tianji.learning.service.IInteractionQuestionService;
import com.tianji.learning.service.IInteractionReplyService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.w3c.dom.html.HTMLImageElement;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 互动问题的回答或评论 服务实现类
 * </p>
 *
 * @author fx
 * @since 2024-08-25
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class InteractionReplyServiceImpl extends ServiceImpl<InteractionReplyMapper, InteractionReply> implements IInteractionReplyService {

    private final UserClient userClient;
    private final IInteractionQuestionService questionService;
    private final RemarkClient remarkClient;
    private final RabbitMqHelper mqHelper;

    /**
     * 01 用户端 管理端-分页查询回答、评论（day07-team-01）
     * 按照是否是学士区分
     *
     * @param pageQuery
     * @param isStudent
     * @return
     */
    @Override
    public PageDTO<ReplyVO> queryReplyPage(ReplyPageQuery pageQuery, Boolean isStudent) {
        // 0 参数校验
        if (ObjectUtils.isEmpty(pageQuery.getQuestionId()) && ObjectUtils.isEmpty(pageQuery.getAnswerId())) {
            throw new BadRequestException("问题id和回答id不能同时为空");
        }
        // 1 根据分页条件查询 注意是否隐藏hidden
        Long questionId = pageQuery.getQuestionId();
        Long answerId = pageQuery.getAnswerId();
        Page<InteractionReply> pageResult = this.lambdaQuery()
                .eq(ObjectUtils.isNotEmpty(questionId), InteractionReply::getQuestionId, questionId)
                //当questionId存在时，表示查询的是问题列表，按照answerId为0查询
                //当answerId存在时，表示查询的是回复列表，按照传入的answerId查询
                .eq(InteractionReply::getAnswerId, ObjectUtils.isNotEmpty(questionId) ? 0l : answerId)
                .eq(isStudent, InteractionReply::getHidden, false)//是学生，则查询非隐藏的评论，不是学生，查询所有
                .page(pageQuery.toMpPage(
                        // 先根据点赞数排序，再根据创建时间排序
                        new OrderItem("liked_times", false),
                        new OrderItem("create_time", false)
                ));// TODO 设置查询排序条件
        List<InteractionReply> records = pageResult.getRecords();
        if (ObjectUtils.isEmpty(records)) {
            return PageDTO.empty(pageResult);
        }

        // 2 获取用户id集合，包括用户id以及被回复用户id
        Set<Long> userIds = new HashSet<>();
        Set<Long> answerIds = new HashSet<>();
        Set<Long> targetReplyIds = new HashSet<>();
        for (InteractionReply record : records) {
            answerIds.add(record.getId());
            userIds.add(record.getTargetUserId());
            targetReplyIds.add(record.getTargetReplyId());
            if (!isStudent || !record.getAnonymity()) {
                userIds.add(record.getUserId());
            }

        }
        // 3.1 根据用户id集合查询信息
        Map<Long, UserDTO> userInfoMap = new HashMap<>();
        List<UserDTO> userInfoList = userClient.queryUserByIds(userIds);
        if (ObjectUtils.isNotEmpty(userInfoList)) {
            userInfoMap = userInfoList.stream().collect(Collectors.toMap(UserDTO::getId, Function.identity()));
        }

        //3.2 查询当前用户对于每个回复的点赞状态
        Set<Long> likedIds = remarkClient.isLiked(answerIds);

        // 3.3 根据targetReplyIds查询被回复评论，是否隐藏
        List<InteractionReply> targerReplyList = this.listByIds(targetReplyIds);
        Map<Long, Boolean> targerReplyInfoMap =new HashMap<>();
        if (ObjectUtils.isNotEmpty(targerReplyList)){
             targerReplyInfoMap = targerReplyList.stream().collect(Collectors.toMap(InteractionReply::getId, InteractionReply::getAnonymity));
        }


        List<ReplyVO> voList = new ArrayList<>();
        // 4 po -》 vo 填充用户信息 回复者，以及被回复的人
        for (InteractionReply record : records) {
            ReplyVO vo = BeanUtils.copyBean(record, ReplyVO.class);
            UserDTO userInfo = userInfoMap.get(record.getUserId());// 评论用户信息
            UserDTO targetUserInfo = userInfoMap.get(record.getTargetUserId());// 被回复的用户信息

            // 注意判断是否为学生
            // 4.2 添加回复人信息
            if (!isStudent || !record.getAnonymity()) {
                if (ObjectUtils.isNotEmpty(userInfo)) {
                    vo.setUserName(userInfo.getName());
                    vo.setUserIcon(userInfo.getIcon());
                    vo.setUserType(userInfo.getType());
                }
            }
            // 4.3 添加被回复用户信息
            Boolean targetReplyAnonymity = targerReplyInfoMap.get(record.getTargetReplyId());
            if (!isStudent || BooleanUtils.isFalse(targetReplyAnonymity)) {
                vo.setTargetUserName(targetUserInfo.getName());
            }
            // 4.4 填充当前用户点赞信息
            //vo.setLiked(likedIds.contains(record.getId()));
            if (likedIds.contains(record.getId())) {
                vo.setLiked(true);
            }

            voList.add(vo);
        }

        // 3 返回pageDTO
        return PageDTO.of(pageResult, voList);
    }

    /**
     * 02 用户端-新增回答、评论（day07-team-02）
     *
     * @param replyFormDTO
     */
    @Override
    @Transactional
    // TODO 添加分布式事务管理
    public void addReply(ReplyDTO replyFormDTO) {
        // 1 dto -》 po
        InteractionReply reply = BeanUtils.copyBean(replyFormDTO, InteractionReply.class);
        // 2 填充userId
        reply.setUserId(UserContext.getUser());
        this.save(reply);

        // 3 问题状态设置为【未查看】，
        // 是回答，   问题回复数+1，修改问题表中最新一次回答
        // 是1级评论，上级回答的回复数+1
        // 是2级评论，上级回答的回复数+1，1级评论回复数+1

        // 是评论，
        // 1级评论、上级回答的回复数+1
        if (ObjectUtils.isNotEmpty(replyFormDTO.getAnswerId())) {
            this.lambdaUpdate()
                    .eq(InteractionReply::getId, replyFormDTO.getAnswerId())
                    .setSql("reply_times = reply_times + 1")
                    .update();

            // 是2级评论，上级回答的回复数+1，1级评论回复数+1
            if (!ObjectUtils.equal(replyFormDTO.getTargetReplyId(), replyFormDTO.getAnswerId())) {
                this.lambdaUpdate()
                        .eq(InteractionReply::getId, replyFormDTO.getTargetReplyId())
                        .setSql("reply_times = reply_times + 1")
                        .update();
            }
        }

        // 4 问题的状态改为 【未查看】 问题回复数 +1
        questionService.lambdaUpdate()
                .eq(InteractionQuestion::getId, replyFormDTO.getQuestionId())
                .setSql("status = 0")
                .setSql(ObjectUtils.isEmpty(replyFormDTO.getAnswerId()), "answer_times = answer_times + 1")//是回答，问题回复数+1
                .set(ObjectUtils.isEmpty(replyFormDTO.getAnswerId()),InteractionQuestion::getLatestAnswerId,reply.getId())//是回答，修改问题表中最新一次回答id
                .update();

        // 5 TODO mq发送消息->积分系统，保存积分记录
        try {
            mqHelper.send(
                    MqConstants.Exchange.LEARNING_EXCHANGE,
                    MqConstants.Key.WRITE_REPLY,
                    PointsMessage.of(UserContext.getUser(), 5)
            );
        } catch (Exception e) {
            log.error("保存回答、评论积分失败，错误原因：{}", e);
        }
    }
}
